#include "../WindowsCompatibility/OpenGL.h"
#include <iostream>
#include <fstream>
#include <math.h>

#include "../Hud.h"
#include "../Renderer.h"

PerformanceTest::PerformanceTest() {
	enabled = false;
	currentIteration = 0;
	recordLimit = 128;
	bg.SetColor(0,0,0,0.5);
	textColor.SetColor(0.9,0.9,0.9,1);
	colors[0].SetColor(1,0,0,1);
	colors[1].SetColor(0,1,0,1);
	colors[2].SetColor(0,0,1,1);
	colors[3].SetColor(1,1,0,1);
	colors[4].SetColor(0,1,1,1);
	colors[5].SetColor(1,0,1,1);
	colors[6].SetColor(1,1,1,1);
}

void PerformanceTest::SetEnabled(bool enabled) {
	this->enabled = enabled;
	if(this->enabled == true) {
		if(Renderer::GetPerfTestWindowHandle() == 0) {
			glutSetWindow(Renderer::GetMainWindowHandle());
			glutInitWindowSize(Renderer::GetActiveScreenW()/2, Renderer::GetActiveScreenH()/2);
			glutInitWindowPosition(glutGet(GLUT_WINDOW_X) + Renderer::GetActiveScreenW() + Renderer::GetActiveScreenW()*0.05, glutGet(GLUT_WINDOW_Y));
			glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE);
			Renderer::SetPerfTestWindowHandle(glutCreateWindow("PerformanceTest"));
			glutSetWindow(Renderer::GetPerfTestWindowHandle());
			glutDisplayFunc(PhysicsLogic::DrawPerfTestWindow);
			glutReshapeFunc(PerformanceTest::ReshapeCallback);
			glClearColor(0.9f, 0.95f, 1.0f, 1.0);
			glShadeModel(GL_SMOOTH);
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glutMainLoop();
			glutSetWindow(Renderer::GetMainWindowHandle());
		}
	} else {
		glutDestroyWindow(Renderer::GetPerfTestWindowHandle());
		glutSetWindow(Renderer::GetMainWindowHandle());
		Renderer::SetPerfTestWindowHandle(0);
	}
}

void PerformanceTest::Start() {
	if(enabled) {
		lastTime = glutGet(GLUT_ELAPSED_TIME);
		sectionTimes << LinkedList<int>();
		while(sectionTimes.Size() > recordLimit && recordLimit != -1) {
			sectionTimes.RemoveFirst();
		}
		++currentIteration;
	}
}

void PerformanceTest::Next(const char* sectionName) {
	if(enabled) {
		int currentTime = glutGet(GLUT_ELAPSED_TIME);
		if(currentIteration == 1) {
			sectionNames << sectionName;
		}
		if(sectionTimes.Empty() == false) {
			sectionTimes.GetLast() << (currentTime - lastTime);
		}
		lastTime = currentTime;
	}
}

void PerformanceTest::PrintToFile() {
	if(currentIteration >= 1) {
		std::ofstream outputFileStream;
		outputFileStream.open("profiling_data.csv");
		for(int i=0; i<sectionNames.Size(); ++i) {
			outputFileStream << sectionNames[i] << ",";
		}

		outputFileStream << std::endl;

		for(int i=0; i<sectionTimes.Size(); ++i) {
			for(int j=0; j<sectionTimes[i].Size(); ++j) {
				outputFileStream << sectionTimes[i][j] << ",";
			}
			outputFileStream << std::endl;
		}
	}
}

void PerformanceTest::RenderLastValues() {
	Hud::Begin();

	glLineWidth(2.0f);

	if(currentIteration >= 2) {
		int screenWidth = Renderer::GetActiveScreenW();
		int screenHeight = Renderer::GetActiveScreenH();

		const float margin = 4;
		const float stripHeight = 20;
		const float textHeight = 12;
		const float graphHeight = 192;
		const float graphXScale = (recordLimit != -1) ? (screenWidth - margin)/recordLimit : 6.0f;
		const int n = sectionTimes.Size();
		const int m = sectionNames.Size();
		
		LinkedList<int>& oneBeforeLast = sectionTimes[n-2];
		for(int i=0; i<oneBeforeLast.Size(); ++i) {
			Renderer::SetMaterial(bg);
			Hud::Rect(margin, margin + i*(stripHeight+margin), screenWidth - margin, margin + i*(stripHeight+margin) + stripHeight);
			Renderer::SetMaterial(colors[i%colorsCount]);
			Hud::Rect(margin, margin + i*(stripHeight+margin), margin + oneBeforeLast[i], margin + i*(stripHeight+margin) + stripHeight);
			Renderer::SetMaterial(textColor);
			Hud::TextBaseBitmap(sectionNames[i], margin, margin + i*(stripHeight+margin) + stripHeight*0.5f + textHeight*0.5f);
		}

		Renderer::SetMaterial(bg);
		Hud::Rect(margin,screenHeight-graphHeight-margin,screenWidth-margin, screenHeight-margin);
		int v1;
		for(int j=0; j<m; ++j) {
			for(int i=0; i<n-1; ++i) {
				if(i==0) {
					v1 = sectionTimes[i][j];
				} else {
					int v2 = sectionTimes[i][j];
					Renderer::SetMaterial(colors[j%colorsCount]);
					Hud::Line(margin+(i-1)*graphXScale, screenHeight-v1-margin, margin+i*graphXScale, screenHeight-v2-margin);
					v1 = v2;
				}
			}
		}
	}

	Hud::End();
}

void PerformanceTest::Reset() {
	currentIteration = 0;
	sectionNames.Clear();
	sectionTimes.Clear();
}

void PerformanceTest::ReshapeCallback(int width, int height) {
	glViewport(0, 0, width, height);
}

